package com.lyb.hardware.memory;

import com.lyb.constant.MemoryConstant;
import com.lyb.constant.SystemConstant;
import com.lyb.hardware.AlpacaPCB;

import java.util.ArrayList;
import java.util.List;

/**
 * 内存类，模拟内存的数据结构
 * 内存分为系统区(自定义大小)和用户区(512字节)
 * 系统区：PCBA(进程PCB存放区域)、MAT(内存分配表)
 * 用户区：可执行文件内容
 * TODO：内存合并
 *
 * @author Mr.Alpaca
 * @version 1.4.0
 */
public class AlpacaMemory {
    /* ********** 系统区 ********** */
    /**
     * Process Control Block Area
     * 所有进程PCB存放区域
     * 最大容量为10
     */
    private final List<AlpacaPCB> pcba;
    /**
     * 内存分配表
     * 表示内存空间的使用情况，每个索引表示内存用户区中的指定行
     * 若值为1表示该空间已被占用，若为0表示该空间空间
     * 长度为内存用户区行数
     */
    private final byte[] mat;

    /* ********** 用户区 ********** */
    /**
     * User Area(用户区)
     * 总长度为512字节，分为64行，每行8字节，用于存储可执行命令
     */
    private final AlpacaUserArea userArea;


    public AlpacaMemory() {
        /* 初始化PCB Area */
        this.pcba = new ArrayList<>();
        for (int i = 0; i < MemoryConstant.PCB_NUM; i++) {
            this.pcba.add(new AlpacaPCB());
        }
        this.mat = new byte[MemoryConstant.USER_AREA_LINE_NUM];
        this.userArea = new AlpacaUserArea();
    }

    /* ********** 读 ********** */

    /**
     * 获取内存中指定索引的内容
     *
     * @param index 指定索引
     * @return 指定索引的内容(字符串格式)
     */
    public String readContent(int index) {
        return userArea.readContent(index);
    }

    /**
     * 获取可用于存储指定数量指令的空闲区
     * 若无则返回null
     *
     * @param size 可执行文件的指令数
     * @return 索引0表示空闲区起始索引，索引1表示结束索引
     */
    public int[] getEmptyArea(int size) {
        int[] indexs = new int[2];
        for (int i = 0; i < mat.length - 1; i++) {
            int count = 0;
            for (int j = i; j < mat.length; j++) {
                if (mat[j] != 0) {
                    break;
                }
                count++;
                if (count == size) {
                    indexs[0] = i;
                    indexs[1] = j;
                    return indexs;
                }
            }
        }
        return null;
    }

    /**
     * 返回内存用户区当前空闲区域大小
     *
     * @return 空闲区域大小
     */
    public int getEmptySize() {
        int count = 0;
        for (byte space : mat) {
            if (space == 0) {
                count++;
            }
        }
        return count * MemoryConstant.USER_AREA_LINE_SIZE;
    }


    /* ********** 写 ********** */

    /**
     * 将内容写入到内存指定索引中
     *
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     * @param content    文件内容
     */
    public void write(int startIndex, int endIndex, String content) {
        /* 修改文件分配表 */
        for (int i = startIndex; i <= endIndex; i++) {
            mat[i] = 1;
        }
        /* 写入PCB指定的用户区范围 */
        userArea.writeContent(startIndex, endIndex, content);
    }


    /* ********** 删除 ********** */

    /**
     * 清除内存指定区域内容
     * 采取"写入时删除(Delete When Write)"策略
     * 通过修改内存分配表来达到逻辑上删除内存指定区域，而真实内容只有在3种情况下会被实际擦除
     * 1.新的内容写入
     * 2.内存空间重新分配
     * 3.内存回收
     *
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     */
    public void clear(int startIndex, int endIndex) {
        for (int i = startIndex; i <= endIndex; i++) {
            mat[i] = 0;
        }
    }

    /**
     * 擦除指定内存范围内容(物理上擦除)
     *
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     */
    private void delete(int startIndex, int endIndex) {
        userArea.clearContent(startIndex, endIndex);
    }


    /* ********** Getter & Setter ********** */

    public List<AlpacaPCB> getPcba() {
        return pcba;
    }

    public byte[] getMat() {
        return mat;
    }
}
